package org.goldenport.g3

import scala.collection.mutable.ArrayBuffer
import scala.xml.Node
import org.goldenport.g3.adapters.PortAdapter
import org.goldenport.g3.messages._
import scalaz._
import Scalaz._
import org.goldenport.sdoc.SDoc
import com.asamioffice.goldenport.text.UString

/**
 * @since   Mar. 19, 2010
 * @version Sep. 18, 2010
 * @version Dec.  4, 2011
 * @author  ASAMI, Tomoharu
 */
class G3Channel(val id: G3ChannelId, val adapter: G3Adapter, context: G3Context) extends G3Pipe(context) {
  context_channel = Some(this)

  private val parcel_history = new ArrayBuffer[G3Parcel]

  def open() {
    adapter.open(context)
  }

  def close() {
    adapter.close()
  }

  def getParcel(): Option[G3Parcel] = parcel_history.headOption

  def getContent(): Option[AnyRef] = {
    parcel_history.headOption.flatMap(p => Some(p.content))
  }

  def postParcel(parcel: G3Parcel) {
    parcel_history += parcel
  }

  def stimulus(signal: AnyRef, session: G3Session): G3Ticket = {
    channel_stimulus(signal, session)
  }

  def send(parcel: G3Parcel): G3Ticket = {
    channel_send(parcel)
  }

  def invoke(parcel: G3Parcel): G3Parcel = {
    channel_invoke(parcel)
  }

  private def channel_stimulus(signal: AnyRef, session: G3Session): G3Ticket = {
    val (uowParcel, ticket) = context.createUnitofworkSignalParcel("Channel Stimulus", this)(signal, session)
    val adaptedParcel = adapter.execute(uowParcel)
    pipe_execute(adaptedParcel)
    ticket
  }

  private def channel_send(parcel: G3Parcel): G3Ticket = {
    val (uowParcel, ticket) = context.createUnitofworkParcel("Channel Send", this)(parcel)
    val adaptedParcel = adapter.execute(uowParcel)
    pipe_execute(adaptedParcel)
    ticket
  }

  private def channel_invoke(parcel: G3Parcel): G3Parcel = {
    val (uowParcel, ticket) = context.createUnitofworkParcel("Channel Invoke", this)(parcel)
    val adaptedParcel = adapter.execute(uowParcel)
    pipe_execute(adaptedParcel)
    context.getResultParcel(ticket).get
  }

  def isComplete(uow: G3UnitofworkId): Boolean = {
    !is_processing(uow)
  }
}

class PlainChannel(id: G3ChannelId, adapter: G3Adapter, context: G3Context) extends G3Channel(id, adapter, context) {
  def this(id: G3ChannelId, context: G3Context) = this(id, NullAdapter, context)
}

class StartChannel(id: G3ChannelId, context: G3Context) extends G3Channel(id, NullAdapter, context) {
  override protected def do_Action(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): AnyRef = {
    get_config_value.getOrElse(content)
  }
}

class SubscribeChannel(id: G3ChannelId, val subscribers: List[String], context: G3Context)
    extends G3Channel(id, NullAdapter, context) {
}

class AgentChannel(id: G3ChannelId, val activity: PartialFunction[AnyRef, AnyRef], context: G3Context)
    extends G3Channel(id, new AgentAdapter(activity), context) {
}

class Agent2Channel(id: G3ChannelId, val activity: G3AgentContext => PartialFunction[AnyRef, AnyRef], context: G3Context)
    extends G3Channel(id, new Agent2Adapter(activity), context) {
}

class ServiceChannel(id: G3ChannelId, val uri: String, context: G3Context) 
    extends G3Channel(id, new ServiceAdapter(uri), context) {
}

class BeanChannel(id: G3ChannelId, val className: String, val methodName: Option[String], context: G3Context)
    extends G3Channel(id, new BeanAdapter(className, methodName), context) {
}

class ComponentChannel(id: G3ChannelId, component: G3Component, context: G3Context)
    extends G3Channel(id, new ComponentAdapter(component), context) {
}

// 2011-12-04
class PortChannel(context: G3Context, id: G3ChannelId, val path: String, val description: Option[Description]) 
    extends G3Channel(id, new PortAdapter(path, description), context) {
  def isAccept(cmd: RestCommand): Boolean = {
    val (_, args, props) = cmd.asOperation
    (for (desc <- description; schema <- desc.schema) yield {
      schema.fields.forall(f => f.multiplicity match {
        case MOne => props.isDefinedAt(f.name.name)
        case MZeroOne => true
        case MOneMore =>props.isDefinedAt(f.name.name) 
        case MZeroMore => true
      })
    }).cata(b => b, true) || !args.isEmpty
  }

  def isPartialAccept(cmd: RestCommand): Boolean = {
    val (_, args, props) = cmd.asOperation
    !props.isEmpty
/*
    (for (desc <- description; schema <- desc.schema) yield {
      !props.isEmpty
    }).cata(b => b, true) || !args.isEmpty
*/
  }

  def relativePath: String = {
    if (path.startsWith("/")) path.substring(1)
    else path
  }

  def title: SDoc = description match {
    case Some(desc) => desc.title
    case None => UString.capitalize(relativePath)
  }

  def subtitle: Option[SDoc] = description.flatMap(_.subtitle)
  def summary: Option[SDoc] = description.flatMap(_.summary)

  def titleAsXml: Node = title.toXml
  def subtitleAsXml: List[Node] = subtitle.toList.map(_.toXml)
  def summaryAsXml: List[Node] = summary.toList.map(_.toXml)
}

/*
class EntityChannel(id: G3ChannelId, context: G3Context) extends G3Channel(id, NullAdapter, context) {
}
*/
